#-------------------------------------------------------------------------------
# Name:        1
# Purpose:
#
# Author:      z93446
#
# Created:     23/12/2015
# Copyright:   (c) z93446 2015
# Licence:     <your licence>
#-------------------------------------------------------------------------------

import Crc

class CModbus:

	def __init__(self, vphy, vslaveAddr, vverbose = False):

		self.phy = vphy
		self.slaveAddr = vslaveAddr
		self.ModbusCRC = Crc.Modbus()
 		self.MEM_MSK = 0X4000
		self.BIWORD_MSK = 0X8000
		self.BIWORD_RAM_MSK = 0XC000
		#self.R_RES_COUNT_OFFSET = 4
		#self.R_RES_VALUE_OFFSET = 5
		self.R_RES_COUNT_OFFSET = 2
		self.R_RES_VALUE_OFFSET = 3
		self.FUNCODE_OFFSET = 1
		self.REG_ADDR_OFFSET = 2
		self.W_RES_VALUE_OFFSET = 4
		self.verbose = vverbose


	def Read(self, Addr, count, IsBiWords = False, IsMem = False):

		data = chr(self.slaveAddr) + '\x03'

		if (Addr & self.MEM_MSK) != 0:
			IsMem = True
		elif IsMem == True:
			Addr = Addr + self.MEM_MSK

		if (Addr & self.BIWORD_MSK) != 0:
			IsBiWords = True
		elif IsBiWords == True:
			Addr = Addr + self.BIWORD_MSK

		data = data  + chr((Addr >> 8) & 0xFF) + chr(Addr & 0xFF)

		data = data  + chr((count >> 8) & 0xFF) + chr(count & 0xFF)

		crc = self.ModbusCRC.Calc(data)
		data = data  + chr(crc & 0xFF) + chr((crc >> 8) & 0xFF)

		self.phy.flushInput()
		self.phy.write(data)
		if self.verbose == True:
			print ">>" + data.encode('hex')


		data = self.phy.read(512)
		if self.verbose == True:
			print "<<" + data.encode('hex')

		crc = self.ModbusCRC.Calc(data[0:len(data) - 2])

		if (chr(crc >> 8) != data[len(data) - 1]) or (chr(crc & 0xFF) != data[len(data) - 2]) :
			return None

		if (ord(data[1]) & 0x80) == 0x80:
			return None

		#Addr = (ord(data[self.REG_ADDR_OFFSET]) << 8) + ord(data[self.REG_ADDR_OFFSET + 1])

		#IsBiWords = (Addr & self.BIWORD_MSK) != 0

		count = ord(data[self.R_RES_COUNT_OFFSET]) / 2;

		if IsBiWords == True:
			count = count / 2

		value = []
		offset = self.R_RES_VALUE_OFFSET

		while count > 0:

			if IsBiWords == True:
				value.append((ord(data[offset]) << 24) + (ord(data[offset+1]) << 16)+(ord(data[offset+2]) << 8)+ord(data[offset+3]))
				offset = offset + 4
			else:
				value.append((ord(data[offset]) << 8)+ord(data[offset+1]))
				offset = offset + 2

			count = count - 1

		return value


	def WriteSingle(self, Addr, value, IsBiWords = False, IsMem = False):

		# Add slave address and function code
		data = chr(self.slaveAddr) + '\x06'

		# Append memory mask bit if required
		if IsMem == True:
			Addr = Addr + self.MEM_MSK

		# Append
		if IsBiWords == True:
			Addr = Addr + self.BIWORD_MSK


		data = data  + chr((Addr >> 8) & 0xFF) + chr(Addr & 0xFF)

		if IsBiWords == False:

			data = data + chr((value >> 8) & 0xFF) + chr(value & 0xFF)

		else:

			data = data + chr((value >> 24) & 0xFF) + chr((value >> 16) & 0xFF) + chr((value >> 8) & 0xFF) + chr(value & 0xFF)

		crc = self.ModbusCRC.Calc(data)
		data = data  + chr(crc & 0xFF) + chr((crc >> 8) & 0xFF)

		self.phy.flushInput()
		self.phy.write(data)
		if self.verbose == True:
			print ">>" + data.encode('hex')

		data = self.phy.read(512)
		if self.verbose == True:
			print "<<" + data.encode('hex')

		crc = self.ModbusCRC.Calc(data[0:len(data) - 2])

		if (chr(crc >> 8) != data[len(data) - 1]) or (chr(crc & 0xFF) != data[len(data) - 2]) :
			return None

		if (ord(data[self.FUNCODE_OFFSET]) & 0x80) == 0x80:
			return None

		Addr = (ord(data[self.REG_ADDR_OFFSET]) << 8) + ord(data[self.REG_ADDR_OFFSET + 1])

		value = None
		offset = self.W_RES_VALUE_OFFSET

		if (Addr & self.BIWORD_MSK) != 0:

			value = (ord(data[offset]) << 24) + (ord(data[offset+1]) << 16) + \
			(ord(data[offset+2]) << 8) + ord(data[offset+3])

		else :

			value = (ord(data[offset]) << 8) + ord(data[offset+1])


		return value

